{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# Backtest: FX bar data\n",
    "\n",
    "Tutorial for [NautilusTrader](https://nautilustrader.io/docs/) a high-performance algorithmic trading platform and event driven backtester.\n",
    "\n",
    "[View source on GitHub](https://github.com/nautechsystems/nautilus_trader/blob/develop/docs/tutorials/backtest_fx_bars.ipynb).\n",
    "\n",
    ":::info\n",
    "We are currently working on this tutorial.\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "This tutorial runs through how to set up a `BacktestEngine` (low-level API) for a single 'one-shot' backtest run using FX bar data."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2",
   "metadata": {},
   "source": [
    "## Prerequisites\n",
    "\n",
    "- Python 3.11+ installed\n",
    "- [JupyterLab](https://jupyter.org/) or similar installed (`pip install -U jupyterlab`)\n",
    "- [NautilusTrader](https://pypi.org/project/nautilus_trader/) latest release installed (`pip install -U nautilus_trader`)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "## Imports\n",
    "\n",
    "We'll start with all of our imports for the remainder of this tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from decimal import Decimal\n",
    "\n",
    "from nautilus_trader.backtest.config import BacktestEngineConfig\n",
    "from nautilus_trader.backtest.engine import BacktestEngine\n",
    "from nautilus_trader.backtest.models import FillModel\n",
    "from nautilus_trader.backtest.modules import FXRolloverInterestConfig\n",
    "from nautilus_trader.backtest.modules import FXRolloverInterestModule\n",
    "from nautilus_trader.config import LoggingConfig\n",
    "from nautilus_trader.config import RiskEngineConfig\n",
    "from nautilus_trader.examples.strategies.ema_cross import EMACross\n",
    "from nautilus_trader.examples.strategies.ema_cross import EMACrossConfig\n",
    "from nautilus_trader.model import BarType\n",
    "from nautilus_trader.model import Money\n",
    "from nautilus_trader.model import Venue\n",
    "from nautilus_trader.model.currencies import JPY\n",
    "from nautilus_trader.model.currencies import USD\n",
    "from nautilus_trader.model.enums import AccountType\n",
    "from nautilus_trader.model.enums import OmsType\n",
    "from nautilus_trader.persistence.wranglers import QuoteTickDataWrangler\n",
    "from nautilus_trader.test_kit.providers import TestDataProvider\n",
    "from nautilus_trader.test_kit.providers import TestInstrumentProvider"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {},
   "source": [
    "## Set up backtest engine"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Initialize a backtest configuration\n",
    "config = BacktestEngineConfig(\n",
    "    trader_id=\"BACKTESTER-001\",\n",
    "    logging=LoggingConfig(log_level=\"ERROR\"),\n",
    "    risk_engine=RiskEngineConfig(\n",
    "        bypass=True,  # Example of bypassing pre-trade risk checks for backtests\n",
    "    ),\n",
    ")\n",
    "\n",
    "# Build backtest engine\n",
    "engine = BacktestEngine(config=config)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7",
   "metadata": {},
   "source": [
    "## Add simulation module\n",
    "\n",
    "We can optionally plug in a module to simulate rollover interest. The data is available from pre-packaged test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "provider = TestDataProvider()\n",
    "interest_rate_data = provider.read_csv(\"short-term-interest.csv\")\n",
    "config = FXRolloverInterestConfig(interest_rate_data)\n",
    "fx_rollover_interest = FXRolloverInterestModule(config=config)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "## Add fill model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10",
   "metadata": {},
   "source": [
    "For this backtest we'll use a simple probabilistic fill model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "fill_model = FillModel(\n",
    "    prob_fill_on_limit=0.2,\n",
    "    prob_fill_on_stop=0.95,\n",
    "    prob_slippage=0.5,\n",
    "    random_seed=42,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12",
   "metadata": {},
   "source": [
    "## Add venue"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13",
   "metadata": {},
   "source": [
    "For this backtest we just need a single trading venue which will be a similated FX ECN."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "SIM = Venue(\"SIM\")\n",
    "engine.add_venue(\n",
    "    venue=SIM,\n",
    "    oms_type=OmsType.HEDGING,  # Venue will generate position IDs\n",
    "    account_type=AccountType.MARGIN,\n",
    "    base_currency=None,  # Multi-currency account\n",
    "    starting_balances=[Money(1_000_000, USD), Money(10_000_000, JPY)],\n",
    "    fill_model=fill_model,\n",
    "    modules=[fx_rollover_interest],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15",
   "metadata": {},
   "source": [
    "## Add instruments and data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16",
   "metadata": {},
   "source": [
    "Now we can add instruments and data. For this backtest we'll pre-process bid and ask side bar data into quotes using a `QuoteTickDataWrangler`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Add instruments\n",
    "USDJPY_SIM = TestInstrumentProvider.default_fx_ccy(\"USD/JPY\", SIM)\n",
    "engine.add_instrument(USDJPY_SIM)\n",
    "\n",
    "# Add data\n",
    "wrangler = QuoteTickDataWrangler(instrument=USDJPY_SIM)\n",
    "ticks = wrangler.process_bar_data(\n",
    "    bid_data=provider.read_csv_bars(\"fxcm/usdjpy-m1-bid-2013.csv\"),\n",
    "    ask_data=provider.read_csv_bars(\"fxcm/usdjpy-m1-ask-2013.csv\"),\n",
    ")\n",
    "engine.add_data(ticks)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {},
   "source": [
    "## Configure strategy"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19",
   "metadata": {},
   "source": [
    "Next we'll configure and initialize a simple `EMACross` strategy we'll use for the backtest."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Configure your strategy\n",
    "config = EMACrossConfig(\n",
    "    instrument_id=USDJPY_SIM.id,\n",
    "    bar_type=BarType.from_str(\"USD/JPY.SIM-5-MINUTE-BID-INTERNAL\"),\n",
    "    fast_ema_period=10,\n",
    "    slow_ema_period=20,\n",
    "    trade_size=Decimal(1_000_000),\n",
    ")\n",
    "\n",
    "# Instantiate and add your strategy\n",
    "strategy = EMACross(config=config)\n",
    "engine.add_strategy(strategy=strategy)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21",
   "metadata": {},
   "source": [
    "## Run backtest\n",
    "\n",
    "We now have everything required to run the backtest. Once the engine has completed running through all the data, a post-analysis report will be logged."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "engine.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23",
   "metadata": {},
   "source": [
    "## Generating reports\n",
    "\n",
    "Additionally, we can produce various reports to further analyze the backtest result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "engine.trader.generate_account_report(SIM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "engine.trader.generate_order_fills_report()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "engine.trader.generate_positions_report()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
